#include <stdio.h>
#include <stdlib.h>
#include "vmath.h"

#define IMAGE_SIZE 800

#define SN_F2 0.366025403f
#define SN_G2 0.211324865f
#define SN_FASTFLOOR(x) ((x) > 0 ? (int)(x) : (int)(x)-1)

static unsigned char g_sn_perm[512] = {
  151,160,137,91,90,15,
  131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
  190,6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
  88,237,149,56,87,174,20,125,136,171,168,68,175,74,165,71,134,139,48,27,166,
  77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
  102,143,54,65,25,63,161,1,216,80,73,209,76,132,187,208,89,18,169,200,196,
  135,130,116,188,159,86,164,100,109,198,173,186,3,64,52,217,226,250,124,123,
  5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
  223,183,170,213,119,248,152,2,44,154,163,70,221,153,101,155,167,43,172,9,
  129,22,39,253,19,98,108,110,79,113,224,232,178,185,112,104,218,246,97,228,
  251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
  49,192,214,31,181,199,106,157,184,84,204,176,115,121,50,45,127,4,150,254,
  138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180,
  151,160,137,91,90,15,
  131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
  190,6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
  88,237,149,56,87,174,20,125,136,171,168,68,175,74,165,71,134,139,48,27,166,
  77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
  102,143,54,65,25,63,161,1,216,80,73,209,76,132,187,208,89,18,169,200,196,
  135,130,116,188,159,86,164,100,109,198,173,186,3,64,52,217,226,250,124,123,
  5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
  223,183,170,213,119,248,152,2,44,154,163,70,221,153,101,155,167,43,172,9,
  129,22,39,253,19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
  251,34,242,193,238,210,144,12,191,179,162,241,81,51,145,235,249,14,239,107,
  49,192,214,31,181,199,106,157,184,84,204,176,115,121,50,45,127,4,150,254,
  138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180 
};

static float g_sn_grad2[8][2] = {
  { -1.0f, -1.0f }, { 1.0f, 0.0f }, { -1.0f, 0.0f }, { 1.0f, 1.0f },
  { -1.0f, 1.0f }, { 0.0f, -1.0f }, { 0.0f, 1.0f }, { 1.0f, -1.0f }
};

static inline void snGrad2(int hash, float *gx, float *gy) {
  int h = hash & 7;
  *gx = g_sn_grad2[h][0];
  *gy = g_sn_grad2[h][1];
}

/* Simplex Noise */
float snoise2(float x, float y) {
  float n0, n1, n2;
  float gx0, gy0, gx1, gy1, gx2, gy2;
  float x1, y1, x2, y2;
  float t0, t20, t40;
  float t1, t21, t41;
  float t2, t22, t42;
  int ii, jj;

  float s = (x + y) * SN_F2;
  float xs = x + s;
  float ys = y + s;
  int i = SN_FASTFLOOR(xs);
  int j = SN_FASTFLOOR(ys);

  float t = (i + j) * SN_G2;
  float X0 = i - t;
  float Y0 = j - t;
  float x0 = x - X0;
  float y0 = y - Y0;

  int i1, j1;
  if (x0 > y0) { i1 = 1; j1 = 0; }
  else { i1 = 0; j1 = 1; }

  x1 = x0 - i1 + SN_G2;
  y1 = y0 - j1 + SN_G2;
  x2 = x0 - 1.0f + 2.0f * SN_G2;
  y2 = y0 - 1.0f + 2.0f * SN_G2;

  ii = i & 0xff;
  jj = j & 0xff;

  t0 = 0.5f - x0 * x0 - y0 * y0;
  t20, t40;
  if (t0 < 0.0f) t40 = t20 = t0 = n0 = gx0 = gy0 = 0.0f;
  else {
    snGrad2(g_sn_perm[ii + g_sn_perm[jj]], &gx0, &gy0);
    t20 = t0 * t0;
    t40 = t20 * t20;
    n0 = t40 * (gx0 * x0 + gy0 * y0);
  }

  t1 = 0.5f - x1 * x1 - y1 * y1;
  t21, t41;
  if (t1 < 0.0f) t21 = t41 = t1 = n1 = gx1 = gy1 = 0.0f;
  else {
    snGrad2(g_sn_perm[ii + i1 + g_sn_perm[jj + j1]], &gx1, &gy1);
    t21 = t1 * t1;
    t41 = t21 * t21;
    n1 = t41 * ( gx1 * x1 + gy1 * y1 );
  }

  t2 = 0.5f - x2 * x2 - y2 * y2;
  t22, t42;
  if (t2 < 0.0f) t42 = t22 = t2 = n2 = gx2 = gy2 = 0.0f;
  else {
    snGrad2(g_sn_perm[ii + 1 + g_sn_perm[jj + 1]], &gx2, &gy2);
    t22 = t2 * t2;
    t42 = t22 * t22;
    n2 = t42 * (gx2 * x2 + gy2 * y2);
  }

  return 40.0f * (n0 + n1 + n2);
}

void ComputeColor(float x, float y, vmVec3 *color) {
  float n = 0.5f + 0.5f * snoise2(4.0f * x, 4.0f * y);
  vmVec3SetFromScalar(color, n);
}

int main(int argc, char *argv[]) {
  unsigned char *image;
  int x, y;
  FILE *fp;

  image = malloc(IMAGE_SIZE * IMAGE_SIZE * 4);
  if (image == NULL) {
    fprintf(stderr, "Failed to allocate memory for the image.\n");
    exit(1);
  }

  for (y = 0; y < IMAGE_SIZE; ++y) {
    for (x = 0; x < IMAGE_SIZE; ++x) {
      int idx = IMAGE_SIZE * y + x;
      vmVec3 color;

      ComputeColor((float)x / IMAGE_SIZE, (float)y / IMAGE_SIZE, &color);
      vmVec3Saturate(&color, &color);

      image[idx * 4 + 2] = (unsigned char)(255.0f * color.x);
      image[idx * 4 + 1] = (unsigned char)(255.0f * color.y);
      image[idx * 4 + 0] = (unsigned char)(255.0f * color.z);
      image[idx * 4 + 3] = 255;
    }
  }

  fp = fopen("terrain.tga", "wb");
  if (fp) {
    unsigned char tga_header[18] = {
      0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      IMAGE_SIZE & 0x00ff, (IMAGE_SIZE & 0xff00) >> 8,
      IMAGE_SIZE & 0x00ff, (IMAGE_SIZE & 0xff00) >> 8,
      32, 0
    };
    fwrite(tga_header, sizeof(tga_header), 1, fp);
    fwrite(&image[0], IMAGE_SIZE * IMAGE_SIZE * 4, 1, fp);
    fclose(fp);
  }

  free(image);
  return 0;
}
